package cn.yogehaoren.foxgitstarter.git;

import cn.yogehaoren.foxgitstarter.properties.GitProperties;
import cn.yogehaoren.foxgitstarter.event.GitPostPullEvent;
import cn.yogehaoren.foxgitstarter.event.GitPrevPullEvent;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.ResetCommand;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
import org.eclipse.jgit.transport.CredentialsProvider;
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.File;
import java.nio.file.Paths;

/**
 * @author WangNing
 * date  2021/8/4 23:31
 * @version 1.0
 */
@Component
@Slf4j
public class FoxGitService implements ApplicationEventPublisherAware {

    @Autowired
    private GitProperties gitProperties;

    private ApplicationEventPublisher applicationEventPublisher;

    private CredentialsProvider credentialsProvider;

    @PostConstruct
    public void initCredentials(){
        credentialsProvider = new UsernamePasswordCredentialsProvider(gitProperties.getUserName(), gitProperties.getPassword());
    }

    /**
     * 检测本地仓库是否存在
     * @return true 存在 false 不存在
     */
    public Repository getRepository() {
        try{

            File repoDir = new File(getLocalGitPath());
            FileRepositoryBuilder builder = new FileRepositoryBuilder();
            Repository repository = builder.setGitDir(repoDir).readEnvironment().findGitDir().build();
            if(!repository.getRefDatabase().getRefs().isEmpty()){
                return repository;
            }else {
                return null;
            }

        }catch (Exception e){
            e.printStackTrace();
            log.error(e.getMessage());
            return null;
        }

    }

    /**
     * 克隆远程仓库
     */
    public void cloneRepository() {

        applicationEventPublisher.publishEvent(new GitPrevPullEvent(this));
        boolean gitCloneSuccess = true;
        Repository repository = getRepository();
        if(repository!=null){
            repository.close();
            log.info("本地仓库已存在，终止克隆");
            return;

        }

        File localPath = new File(gitProperties.getLocalPath());

        try {
            Git.cloneRepository()
                    .setURI(gitProperties.getRemoteUrl())
                    .setDirectory(localPath)
                    .setCredentialsProvider(credentialsProvider)
                    .setProgressMonitor(new GitCloneConfigMonitor())
                    .call();

            log.info("Clone Over!");
        } catch (GitAPIException e) {
            log.error("克隆仓库错误: {}",e.getMessage());
            gitCloneSuccess = false;
        }
        applicationEventPublisher.publishEvent(new GitPostPullEvent(this, gitCloneSuccess));

    }

    /**
     * 同步远程仓库
     */
    public boolean pullRepository() {

        applicationEventPublisher.publishEvent(new GitPrevPullEvent(this));

        Repository repository = getRepository();
        if(repository==null){
            log.info("本地仓库不存在，请先Clone");
            applicationEventPublisher.publishEvent(new GitPostPullEvent(this, false));
        }else {
            try (Git git = new Git(repository)) {

                try {

                    git.pull().setCredentialsProvider(credentialsProvider).call();
                    log.info("正在从[{}]同步数据", gitProperties.getRemoteUrl());
                    applicationEventPublisher.publishEvent(new GitPostPullEvent(this, true));
                    return true;
                } catch (GitAPIException e) {
                    log.error("同步数据失败 {}", e.getMessage());
                    applicationEventPublisher.publishEvent(new GitPostPullEvent(this, false));
                    return false;
                }

            }finally {
                repository.close();
            }
        }
        return false;

    }

    /**
     * 强制同步
     */
    public void forcePullRepository() {

        applicationEventPublisher.publishEvent(new GitPrevPullEvent(this));
        boolean forcePullSuccess = true;
        Repository repository = getRepository();
        if(repository==null){

            log.info("本地仓库不存在，请先Clone");

        }else {
            try (Git git = new Git(repository)) {

                try {
                    log.info("!!!正在强制从[{}]同步数据!!!", gitProperties.getRemoteUrl());
                    git.fetch().setCredentialsProvider(credentialsProvider).setCheckFetchedObjects(true).call();
                    git.reset().setMode(ResetCommand.ResetType.HARD).setRef("origin/master").call();
                    git.pull().setCredentialsProvider(credentialsProvider).call();

                } catch (GitAPIException e) {
                    log.error("同步数据失败 {}", e.getMessage());
                    forcePullSuccess =false;
                }

            }finally {
                repository.close();
            }
        }

        applicationEventPublisher.publishEvent(new GitPostPullEvent(this, forcePullSuccess));
    }

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }

    /**
     * @return 获取本地仓库路径
     */
    private String getLocalGitPath(){
        return Paths.get(gitProperties.getLocalPath(), ".git").toString();

    }
}
